﻿using System;
using System.Collections.Generic;
using System.Threading;
using SimpleServiceBus.EndpointManagement.Server;
using SimpleServiceBus.EndpointManagement.Server.Configuration;
using SimpleServiceBus.Messages.Management.Monitoring;
using SimpleServiceBus.Persistence.Domain.Management.Repositories;
using SimpleServiceBus.Tests.Stubs;
using SimpleServiceBus.Utilities;
using NUnit.Framework;
using NUnit.Framework.SyntaxHelpers;


namespace SimpleServiceBus.Tests.Server
{
    [TestFixture]
    public class EndpointStatusAggregationServiceTestFixture
    {
        private static TestEndpoint CreateEndpoint()
        {
            var endpoint = TestEndpoint.ConfigureEndpoint();
            
            EndpointMonitorConfiguration.ConfigureEndpointAsEndpointMonitor(endpoint,
                                                                            TimeSpan.FromMinutes(10),
                                                                            TimeSpan.FromMinutes(10));

            endpoint.RegisterService<IEndpointStatusRepository, TestStatusRepository>();

            return endpoint.Endpoint as TestEndpoint;
        }

        private static EndpointStatusUpdateNotification CreateUpdate(string epId, params EndpointMeasurement[] measurements)
        {
            var notification = new EndpointStatusUpdateNotification
                                   {
                                       EndpointID = epId,
                                       EndpointMeasurements = new List<EndpointMeasurement>(measurements)
                                   };
            return notification;
        }

        private static EndpointMeasurement CreateMeasurement(string name, object value)
        {
            var m = new EndpointMeasurement
                        {
                            Name = name,
                            UnitOfMeasurement = "Unit",
                            Value = value.ToString()
                        };
            if (value is bool)
            {
                m.CanAggregate = false;
                m.DataType = MeasurementDataType.Boolean;
            }
            else if (value.GetType().IsValueType)
            {
                m.CanAggregate = true;
                m.DataType = MeasurementDataType.Numeric;
            }
            else
            {
                m.CanAggregate = false;
                m.DataType = MeasurementDataType.Text;
            }

            return m;
        }

        [Test]
        public void ProcessIncomingMessages()
        {
            TestEndpoint endpoint = CreateEndpoint();
            var bus = (TestBus) endpoint.MessageBus;
            bus.PublishedMessages.Clear();

            var svc =
                endpoint.LocalServices.GetService<IEndpointStatusAggregationService>();


            endpoint.Start();

            svc.EndpointMonitor.UpdateEndpointStatus(CreateUpdate("testEp1", CreateMeasurement("testM1", 2),
                                                                  CreateMeasurement("testM2", false)));
            svc.EndpointMonitor.UpdateEndpointStatus(CreateUpdate("testEp2", CreateMeasurement("testM3", 5.5),
                                                                  CreateMeasurement("testM4", "hello")));
            Thread.Sleep(50);

            var r = (TestStatusRepository) endpoint.LocalServices.GetService<IEndpointStatusRepository>();
            Assert.That(r.StatusList.Count, Is.EqualTo(2));

            svc.StatusBroadcastInterval = TimeSpan.FromMilliseconds(250);
            Thread.Sleep(350);

            Assert.That(bus.PublishedMessages.Count, Is.GreaterThan(0));
            foreach (SentMessage message in bus.PublishedMessages)
            {
                Assert.That(message.Message, Is.TypeOf(typeof (EndpointStatusSummaryNotification)));
            }

            var summary = bus.LastPublished.Message as EndpointStatusSummaryNotification;
            Assert.IsNotNull(summary);
            Assert.That(summary.ActiveEndpoints.Count, Is.EqualTo(2));

            svc.EndpointMonitor.UpdateEndpointStatus(new EndpointOfflineNotification {EndpointID = "testEp2"});

            Thread.Sleep(350);

            summary = bus.LastPublished.Message as EndpointStatusSummaryNotification;
            Assert.IsNotNull(summary);
            Assert.That(summary.ActiveEndpoints.Count, Is.EqualTo(0));

            svc.EndpointMonitor.UpdateEndpointStatus(CreateUpdate("testEp1", CreateMeasurement("testM1", 1),
                                                                  CreateMeasurement("testM2", false)));
            svc.EndpointMonitor.UpdateEndpointStatus(CreateUpdate("testEp2", CreateMeasurement("testM3", 10.5),
                                                                  CreateMeasurement("testM4", "hello")));

            Thread.Sleep(100);

            summary = bus.LastPublished.Message as EndpointStatusSummaryNotification;
            Assert.IsNotNull(summary);
            Assert.That(summary.ActiveEndpoints.Count, Is.EqualTo(2));

            Assert.That(summary.ActiveEndpoints[0].StatusMeasurements.Count, Is.EqualTo(2));
            Assert.That(summary.ActiveEndpoints[0].StatusMeasurements[0].Average, Is.EqualTo(1.0));
            Assert.That(summary.ActiveEndpoints[0].StatusMeasurements[1].Average, Is.EqualTo(0.0));
            Assert.That(summary.ActiveEndpoints[1].StatusMeasurements[0].Average, Is.EqualTo(10.5));
            Assert.That(summary.ActiveEndpoints[1].StatusMeasurements[1].LastMeasurement.Value, Is.EqualTo("hello"));
            Assert.That(summary.ActiveEndpoints[1].StatusMeasurements[1].Average, Is.EqualTo(0.0));

            endpoint.Stop();
        }

        [Test]
        public void StartStopTest()
        {
            TestEndpoint endpoint = CreateEndpoint();

            IBackgroundService svc = endpoint.LocalServices.GetService<IEndpointStatusAggregationService>();
            endpoint.Start();
            Assert.IsTrue(svc.IsRunning);
            endpoint.Stop();
            Assert.IsFalse(svc.IsRunning);
        }
    }
}